package com.wiscamp.ninechapters.problems.infrastructure.repositories;

import com.wiscamp.ninechapters.common.persistence.DbHelper;
import com.wiscamp.ninechapters.problems.domain.models.exams.PaperPart;
import com.wiscamp.ninechapters.problems.domain.models.exams.PaperSection;
import com.wiscamp.ninechapters.problems.domain.repositories.PaperPartRepository;
import com.wiscamp.ninechapters.problems.infrastructure.converters.PaperPartConverter;
import com.wiscamp.ninechapters.problems.infrastructure.converters.PaperSectionConverter;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.exams.PaperPartMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.exams.PaperPartViewMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.exams.PaperSectionMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.exams.PaperSectionViewMapper;
import com.wiscamp.ninechapters.problems.infrastructure.po.exams.PaperPartPO;
import com.wiscamp.ninechapters.problems.infrastructure.po.exams.PaperSectionPO;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;

@Repository
public class PaperPartRepositoryImpl extends BaseRepository implements PaperPartRepository {
    private final PaperPartMapper paperPartMapper;

    private final PaperSectionMapper paperSectionMapper;

    private final PaperPartViewMapper paperPartViewMapper;

    private final PaperSectionViewMapper paperSectionViewMapper;

    public PaperPartRepositoryImpl(PaperPartMapper paperPartMapper, PaperSectionMapper paperSectionMapper, PaperPartViewMapper paperPartViewMapper, PaperSectionViewMapper paperSectionViewMapper) {
        this.paperPartMapper = paperPartMapper;
        this.paperSectionMapper = paperSectionMapper;
        this.paperPartViewMapper = paperPartViewMapper;
        this.paperSectionViewMapper = paperSectionViewMapper;
    }

    //region paper part
    @HandleDataException
    private PaperPartPO getPaperPartPO(@Min(0) long partId) {
        return PersistenceUtils.findById(partId, paperPartMapper::findById);
    }

    /**
     * Gets a paper part
     *
     * @param partId
     * @return
     */
    @HandleDataException
    public PaperPart getPaperPart(@Min(0) long partId) {
        var part = PersistenceUtils.findEntityById(partId, paperPartViewMapper::findById,
                PaperPartConverter.Instance::toPaperPartByView);
        if (Objects.nonNull(part)) {
            part.setSections(getPaperSectionsByPart(partId));
        }
        return part;
    }

    /**
     * Gets a paper part
     *
     * @param examId
     * @return
     */
    @HandleDataException
    public Collection<PaperPart> getPaperPartsByExam(@Min(0) long examId) {
        var parts = PersistenceUtils.getEntitiesById(examId, paperPartViewMapper::findListByExam,
                PaperPartConverter.Instance::toPaperPartByView);
        var partIds = parts.stream().map(PaperPart::getPartId).collect(Collectors.toList());
        var sections = getPaperSectionsByPartIds(partIds);
        for (var part : parts) {
            part.setSections(sections.stream().filter(p -> p.getPartId() == part.getPartId()).collect(Collectors.toList()));
        }
        return parts;
    }

    /**
     * Creates a paper part
     *
     * @param part
     * @return
     */
    @HandleDataException
    public PaperPart savePaperPart(@NotNull PaperPart part) {
        var newId = part.getPartId();
        if (part.getPartId() <= 0) {
            var newPaperPart = PersistenceUtils.createEntity(part, paperPartMapper::saveAndFlush,
                    PaperPartConverter.Instance::toPaperPartPO, PaperPartConverter.Instance::toPaperPart);
            newId = newPaperPart.getPartId();
        } else {
            var existedPO = getPaperPartPO(part.getPartId());
            if (existedPO == null) existedPO = new PaperPartPO();
            PersistenceUtils.updateEntity(part, existedPO, paperPartMapper::saveAndFlush,
                    PaperPartConverter.Instance::toPaperPartPO, PaperPartConverter.Instance::toPaperPart);
        }

        return getPaperPart(newId);
    }

    /**
     * Deletes a paper part
     *
     * @param partId
     * @return
     */
    @HandleDataException
    public boolean deletePaperPart(@Min(0) long partId) {
        return PersistenceUtils.delete(partId, paperPartMapper::deleteById);
    }
    //endregion

    //region paper section
    @HandleDataException
    private PaperSectionPO getPaperSectionPO(@Min(0) long sectionId) {
        return PersistenceUtils.findById(sectionId, paperSectionMapper::findById);
    }


    /**
     * Gets a paper section
     *
     * @param sectionId
     * @return
     */
    @HandleDataException
    public PaperSection getPaperSection(@Min(0) long sectionId) {
        return DbHelper.findEntityById(sectionId, paperSectionViewMapper::findById,
                PaperSectionConverter.Instance::toPaperSectionByView);
    }

    /**
     * Gets a paper section
     *
     * @param partId
     * @return
     */
    @HandleDataException
    public Collection<PaperSection> getPaperSectionsByPart(@Min(0) long partId) {
        return PersistenceUtils.getEntitiesById(partId, paperSectionViewMapper::findListByPart,
                PaperSectionConverter.Instance::toPaperSectionByView);
    }

    @HandleDataException
    public Collection<PaperSection> getPaperSectionsByPartIds(@NotEmpty Collection<Long> partIds) {
        return PersistenceUtils.getEntitiesByIds(partIds, paperSectionViewMapper::findListByParts,
                PaperSectionConverter.Instance::toPaperSectionByView);
    }


    /**
     * Creates a paper section
     *
     * @param section
     * @return
     */
    @HandleDataException
    public PaperSection savePaperSection(@NotNull PaperSection section) {
        var newId = section.getSectionId();
        if (section.getSectionId() <= 0) {
            var newPaperSection = PersistenceUtils.createEntity(section, paperSectionMapper::saveAndFlush,
                    PaperSectionConverter.Instance::toPaperSectionPO, PaperSectionConverter.Instance::toPaperSection);
            newId = newPaperSection.getSectionId();
        } else {
            var existedPO = getPaperSectionPO(section.getSectionId());
            if (existedPO == null) existedPO = new PaperSectionPO();
            PersistenceUtils.updateEntity(section, existedPO, paperSectionMapper::saveAndFlush,
                    PaperSectionConverter.Instance::toPaperSectionPO, PaperSectionConverter.Instance::toPaperSection);
        }

        return getPaperSection(newId);
    }

    /**
     * Deletes a paper section
     *
     * @param sectionId
     * @return
     */
    @HandleDataException
    public boolean deletePaperSection(@Min(0) long sectionId) {
        return PersistenceUtils.delete(sectionId, paperSectionMapper::deleteById);
    }
    //endregion
}
